Introduction

This competition’s main focus is to forecast future restaurant visitors on a given date, or in short a Time Series problem. We will make our predictions using the data from two different systems:

There will be additional store information provided, regarding their specific locations (lat, long), prefecture, the food or cuisine they are serving.

TL;DR: This kernel is a long due project for me. When the competition was first announced, I was so excited because the restaurant industry was the one that started my journey in Analytics/Data Science. So despite the enormous amount of school workload I’m having right now, I managed to squeeze some time out for it. Actually, my next exam is for Time Series, so I’m trying my best to use what I’ve learned from class!

Loading Data

air_visit_data <- fread('./data/air_visit_data.csv')
air_reserve <- fread('./data/air_reserve.csv')
hpg_reserve <- fread('./data/hpg_reserve.csv')
date_info <- fread('./data/date_info.csv')
hpg_store_info <- fread('./data/hpg_store_info.csv')
air_store_info <- fread('./data/air_store_info.csv')
store_id_relation <- fread('./data/store_id_relation.csv')# add date of week

Simple Feature Engineering

# split date and time
air_reserve$visit_date <- as.Date(air_reserve$visit_datetime)
air_reserve$visit_time <- format(strptime(air_reserve$visit_datetime,
                                   format="%Y-%m-%d %H:%M:%S"), "%H:%M")
air_reserve$reserve_date <- as.Date(air_reserve$reserve_datetime)
air_reserve$reserve_time <- format(strptime(air_reserve$reserve_datetime,
                                   format="%Y-%m-%d %H:%M:%S"), "%H:%M") 
hpg_reserve$visit_date <- as.Date(hpg_reserve$visit_datetime)
hpg_reserve$visit_time <- format(strptime(hpg_reserve$visit_datetime,
                                   format="%Y-%m-%d %H:%M:%S"), "%H:%M")
hpg_reserve$reserve_date <- as.Date(hpg_reserve$reserve_datetime)
hpg_reserve$reserve_time <- format(strptime(hpg_reserve$reserve_datetime,
                                   format="%Y-%m-%d %H:%M:%S"), "%H:%M") 
date_info$calendar_date <- as.Date(date_info$calendar_date)
air_visit_data$visit_date <- as.Date(air_visit_data$visit_date)
# add date of week
air_reserve <- air_reserve %>% 
  mutate(visit_dow=wday(visit_date, label=TRUE)) %>% 
  mutate(reserve_dow=wday(reserve_date, label=TRUE))
hpg_reserve <- hpg_reserve %>% 
  mutate(visit_dow=wday(visit_date, label=TRUE)) %>% 
  mutate(reserve_dow=wday(reserve_date, label=TRUE))
air_visit_data <- air_visit_data %>% 
  mutate(visit_dow=wday(visit_date, label=TRUE))
# add holiday flags
air_reserve <- air_reserve %>% left_join(date_info, by=c("visit_date"="calendar_date"))
hpg_reserve <- hpg_reserve %>% left_join(date_info, by=c("visit_date"="calendar_date"))
air_visit_data <- air_visit_data %>% left_join(date_info, by=c("visit_date"="calendar_date"))
air_reserve$holiday_flg <- factor(air_reserve$holiday_flg, labels=c("Not Holiday", "Holiday"))
hpg_reserve$holiday_flg <- factor(hpg_reserve$holiday_flg, labels=c("Not Holiday", "Holiday"))
air_visit_data$holiday_flg <- factor(air_visit_data$holiday_flg, labels=c("Not Holiday", "Holiday"))

Non-Time-Series Analysis

Store Count

Overall, we have 829 unique air stores that we need to forecast their number of visitors in the Golden Week. Amongst those stores, only 314 stores have reservation information. We are also given the information from Hot Pepper Gourmet with data of over 13,000 stores which is a much greater number of observations comparing to Air. Yet, note that not all Air restaurants have information in the HPG system. Only 150 of them have reservation data and 63 of them have store basic information on HPG.

[1] "air_reserve contains reservation info of 314 stores"
[1] "air_visit contains actual visitors info of 829 stores"
[1] "air_store_info contains actual visitors info of 829 stores"
[1] "hpg_reserve contains reservation info of 13325 stores but only 150 belongs to the AirREGI system"
[1] "hpg_store_info contains reservation info of 4690 stores but only 63 belongs to the AirREGI system"

Store Categories

The two dataset also have restaurant genres albeit they are categorized differently. Let’s compare the actual number of visitors to the Air stores based on the categories of Air vs. HPG. Izakaya (which is an amazing Japanese dining experience where you are served with small dishes while enjoying the drinks with your friends) is the most popular for the AirREGI system – which aligns with the #1 spot on the HPG ranks as well.

However, while cafe/sweets hold the second place in Air, it only has a few number of visitors based on the category of HPG. This suggests the categories of the two systems may not be in sync as we hope they would be.

Correlation Between Restaurant Categories

df1 <- air_store_info %>% 
  left_join(air_visit_data, c("air_store_id")) %>% 
  dplyr::group_by(air_genre_name, visit_date) %>% 
  dplyr::summarize(act_visitor_tol = sum(visitors)) %>% 
  dcast(visit_date ~ air_genre_name) %>% 
  select(-c(Asian)) %>% # more than half are NA entries
  select(-contains("Karaoke/Party")) %>% # more than half are NA entries
  select(-contains("International cuisine")) # more than half are NA entries

# impute missing values
df1 <- na.seadec(df1, algorithm="interpolation")
tsdist <- t(select(df1, -1)) %>% scale() %>% diss("ACF", p=0.05)
hc <- hclust(tsdist)

dend <- as.dendrogram(hc)
dend <- dend %>%
  dendextend::set("branches_k_color", k=4) %>% 
  dendextend::set("branches_lwd", 1.0) %>%
  dendextend::set("labels_cex", 1.2) %>% 
  dendextend::set("leaves_pch", 19) %>%  
  dendextend::set("leaves_col", c("black")) %>%
  dendextend::set("leaves_cex", 0.6) %>% 
  highlight_branches_col

par(cex=0.4)
ggplot(dend, horiz=TRUE) 

Store Locations

Maybe it’s a good time to learn a little bit about the Japanese geography! First, there are 47 different ken (県) or prefectures, which is the most highest level geographic division of Japan. Its closest analogy is the state of the United States. Three other special prefecture designations are to (都) for Tokyo, (道) for Hokkaidō and fu (府) for Osaka and Kyoto.

Following the prefecture, we have shi (市) for cities and then, ku (区) or wards. For larger cities such as Tokyo or Kyoto, their addresses do not follow the above hierachy.

######################################################################################################
# Reference Code: https://www.kaggle.com/captcalculator/a-very-extensive-recruit-exploratory-analysis 
######################################################################################################
# create sp object
jmap <- map("japan", col=1, plot=FALSE, fill=TRUE)
jmap_id <-sapply(strsplit(jmap[['names']], ":"), function(x) x[1])
p4s <- CRS("+proj=longlat +datum=WGS84")
jsp <- map2SpatialPolygons(jmap, ID=jmap_id, proj4string=p4s)
# create SpatialPolygonsDataFrame
jspdf <- data.frame(prefecture=names(jsp))
rownames(jspdf) <- names(jsp)
jspdf <- SpatialPolygonsDataFrame(jsp, jspdf)
# rename columns
air_store_summ['prefecture'] <- c("Fukuoka", "Hiroshima", "Hokkaido", "Hyogo", "Miyagi", "Niigata", "Osaka", "Shizuoka", "Tokyo")
jspdf <- sp::merge(jspdf, air_store_summ, by=c("prefecture"))
jspdf <- sp::merge(jspdf, hpg_store_summ[hpg_store_summ!="None",], by=c("prefecture"))

 

In the data, we’re given the three components of a restaurant’s address, so let’s split them out so we could see a high-level summary of the store locations. Unsurprisingly, Tokyo has the most stores across the country, but on average, it has one of the lowest number of visitors per store.

First Look at the Time Series

Let’s first look at the time series for the number of visitors from the AirREGI system. There are some repeated patterns we could see here (frequent spiles that repeated every 5 days) – which could have been due to the heavy traffic of weekend. In addition, there is a sudden increase in the number of visitors starting around July 1st 2017. Usually such large shift in data values is due to a systematic change. It could be because of AirREGI internal reporting system or maybe, they merged with a large group of restaurants. We are unsure of the cause for such change but it seems like we will have to handle some missing data in this case.

 

# Air Reserved Visitors
air1 <- air_reserve %>%
  group_by(visit_date) %>% summarise(reserve_visit_tol = sum(reserve_visitors))
air2 <- air_visit_data %>% group_by(visit_date) %>% summarise(act_visit_tol = sum(visitors))
rect <- data.frame(xmin=as.Date("2016-07-01", "%Y-%m-%d"),
                   xmax=as.Date("2016-07-10", "%Y-%m-%d"), ymin=-Inf, ymax=Inf)
p1 <- air_visit_data %>%
  group_by(visit_date) %>% summarise(act_visitor_tol = sum(visitors))  %>% 
  ggplot() +
  geom_line(aes(x=visit_date, y=act_visitor_tol), size=0.2, col='grey25') +
  labs(title="Actual Vistiors - AirREGI") + 
  scale_x_date(date_breaks = "1 month") + 
  theme(axis.text.x = element_text(angle=90, hjust=1, size=7)) +
  geom_rect(data=rect, aes(xmin=xmin, xmax=xmax, ymin=ymin, ymax=ymax),
              colour="blue", fill="blue", 
              alpha=0.05, size=0.5, linetype=2, inherit.aes = FALSE)
rect <- data.frame(xmin=as.Date("2016-08-01", "%Y-%m-%d"),
                   xmax=as.Date("2016-10-20", "%Y-%m-%d"), ymin=-Inf, ymax=Inf)
p2 <- air1 %>% 
  ggplot() +
  geom_line(aes(x=visit_date, y=reserve_visit_tol), size=0.2, col='steelblue') +
  geom_rect(data=rect, aes(xmin=xmin, xmax=xmax, ymin=ymin, ymax=ymax),
              colour="blue", fill="blue", 
              alpha=0.05, size=0.5, linetype=2, inherit.aes = FALSE) + 
  labs(title="Actual & Reserved Vistiors - AirREGI") + 
  scale_x_date(date_breaks = "1 month") + 
  theme(axis.text.x = element_text(angle=90, hjust=1))
p <- subplot(p1, p2, nrows = 2, shareX=TRUE)
ggplotly(p)

  Such abnormal behavior can also be seen in the number of visitors reserved on AirREGI. The month of August and September was technically “dead” with little to none reservations before it picked up again in November. The volume of reserved visitors post-November is also at much larger order of magnitude versus pre-August 2016.

In addition, notice that the proportion of reservations to the actual visits is pretty low, between 10-30% on average (excluding the time gap in August).  

Below is the HPG reserve pattern. Herein, the traffic for August and September is not much different and excluding the Christmas-NYE spikes, there is no significant shift like the one we have seen in AirREGI. It could be deduced now that it’s an internal factor that caused such change in pattern AirREGI’s number of visitors rather than an exogenous / external factor.

Air Visitors Time Series Decomposition

Any time series data could be decomposed into at least one of the following components:

  1. Trend component: The overall trend/pattern of the series
  2. Seasonal component: The fluctuations in the data related to calendar cycles
  3. Random component: The remainder that cannot be attributed to seasonal or trend components
daily_visit <- air1 %>% left_join(air2, c("visit_date")) %>% 
  select(reserve_visit_tol) %>% 
  ts(start=c(2016,1,1), frequency=7)
# Daily Visit Rates Decomposition
p2 <- decompose(daily_visit) %>% .$trend %>%
  autoplot(main="Air Visitor Trend Component", xlab="Day") +
  theme(plot.title = element_text(size=8), axis.title=element_text(size=8))
p3 <- decompose(daily_visit) %>% .$seasonal %>%
  autoplot(main="Air Visitor Seasonal Component", xlab="Day")+
  theme(plot.title = element_text(size=8), axis.title=element_text(size=8))
p4 <- decompose(daily_visit) %>% .$random %>%
  autoplot(main="Air Visitor Random Component", xlab="Day")+
  theme(plot.title = element_text(size=8), axis.title=element_text(size=8))
grid.arrange(p2,p3,p4,ncol=3)

The key idea of a time series forecast tend to focus the concept of a “stationary” time series. A stationary time series is when the relationship between observations at different time points are not dependent on a specific time, and they will distribute evenly around a constant mean. Many time series may initially seem to be non-stationary, but can be mathematical transformed into a stationary version.

Is there seasonal pattern in the number of reserved visitors?

By hour or by day of week?

On a non-holiday, there are significantly more customers making reservations to eat out at AirREGI on Friday and Saturday. However, the trend does not follow when it’s a holiday, when we see a significant drop in the total number of visitors. This is highly likely because there are less holidays that fall on a Saturday. Assuming people will be on time for their reservations, we could also see that most of the reservations are pretty early for dinner time, around 5:00PM-6:00PM, and not around 8-9:30PM (Hopefully everyone can translate the military time in the chart below ;) )

p1 <- air_visit_data %>%
  group_by(visit_dow, holiday_flg) %>%
  summarise(visitor_tol = sum(visitors)) %>%
  ggplot(aes(color=holiday_flg)) +
  facet_grid(holiday_flg~., scales="free") +
  geom_line(aes(x=visit_dow, y=visitor_tol), group=1, size=0.3) +
  geom_point(aes(x=visit_dow, y=visitor_tol), size=1) +
  scale_color_manual(values=c("#007D00", "#C00000")) +
  labs(title="Visitors by Day of Week")+
  guides(color=FALSE)
p2 <- air_reserve %>% group_by(reserve_time, visit_dow) %>%
  summarise(visitor_tol = sum(reserve_visitors)) %>% 
  ggplot() + 
  geom_col(aes(x=reserve_time, y=visitor_tol), fill="#19417D") +
  labs(title="Visitors by Day of Week & Time Slot")+
  scale_y_continuous(breaks=seq(0, 65000, by=10000)) +
  theme(axis.text.x=element_text(angle=90, hjust=1, vjust=0.9))
subplot(p1,p2, margin = 0.05)

Or does it follow a monthly trend?

As we could see in the plot below, March and December, which are greatly contributed to the number of holidays within these two months, i.e. the cherry blossom and Christmas-NYE season in Japan. In addition, August in September has a sudden drop as we have observed in the above plot. Given the different level in number of reservations by month, we could suspect that the time series is also driven by a seasonal component.

In order to understand how variables at different time steps are related, we often use the ACF (Autocorrelation Function) and PACF (Partial Autocorrelation Function) plots. Contradictory to what we suspected above, the time series have ingsignificant, if not, non-existent seasonal component based on the ACF plot of a de-trended series below. That being said, the series after the transformation can be defined as “stationary” - i.e. its mean and variance does not vary across time. In the second ACF plot, the only significant spike is at lag 1, meaning that only the pair of the error terms that are one lag different has strong autocorrelation.

# convert air_reserve_visit to ts
air_visit_ts <- ts(air_visit_data[,"visitors"], start=c(2016-01-01), frequency=7) 
# taking the first difference
d.air_visit_ts <- diff(air_visit_ts)

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQKICBodG1sX2RvY3VtZW50OiBkZWZhdWx0Ci0tLQoKYGBge3Igc2V0dXAsIGVjaG89RkFMU0UsIGluY2x1ZGU9RkFMU0UsIG91dC53aWR0aD0iMTAwJSJ9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSkKYGBgCgoKYGBge3IsIGluY2x1ZGU9RkFMU0V9CmxpYnJhcnkoJ2dncGxvdDInKQpsaWJyYXJ5KCdzY2FsZXMnKSAKbGlicmFyeSgnUkNvbG9yQnJld2VyJykgCmxpYnJhcnkoJ3Bsb3RseScpCmxpYnJhcnkoJ2xlYWZsZXQnKQpsaWJyYXJ5KCdodG1sdG9vbHMnKQpsaWJyYXJ5KCdkZW5kZXh0ZW5kJykKCmxpYnJhcnkoJ21hcGRhdGEnKQpsaWJyYXJ5KCdtYXB0b29scycpCgpsaWJyYXJ5KCdkcGx5cicpIApsaWJyYXJ5KCdkYXRhLnRhYmxlJykgCmxpYnJhcnkoJ3RpZHlyJykgCmxpYnJhcnkoJ3N0cmluZ3InKSAKCmxpYnJhcnkoJ2dnZm9ydGlmeScpIApsaWJyYXJ5KCdnZ3JlcGVsJykgCmxpYnJhcnkoJ2dncmlkZ2VzJykgCmxpYnJhcnkoJ2dyaWRFeHRyYScpIApsaWJyYXJ5KCJnZ2RlbmRybyIpCgpsaWJyYXJ5KCdsdWJyaWRhdGUnKQpsaWJyYXJ5KCd0aW1lRGF0ZScpIApsaWJyYXJ5KCd0c2VyaWVzJykKbGlicmFyeSgnaW1wdXRlVFMnKQojIGxpYnJhcnkoJ1RTY2x1c3QnKQpsaWJyYXJ5KCdmb3JlY2FzdCcpIApsaWJyYXJ5KCdwcm9waGV0JykgCgp0aGVtZV9zZXQodGhlbWVfbGlnaHQoKQogICAgICAgICAgK3RoZW1lKGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT03KSwgCiAgICAgICAgICAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KHNpemU9NyksCiAgICAgICAgICAgICAgICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF90ZXh0KHNpemU9NyksCiAgICAgICAgICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTkpLAogICAgICAgICAgICAgICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT05KSkpCgojcm0obGlzdD1scygpKQpgYGAKCiMjIEludHJvZHVjdGlvbgoKVGhpcyBjb21wZXRpdGlvbidzIG1haW4gZm9jdXMgaXMgdG8gZm9yZWNhc3QgZnV0dXJlIHJlc3RhdXJhbnQgdmlzaXRvcnMgb24gYSBnaXZlbiBkYXRlLCBvciBpbiBzaG9ydCBhIFRpbWUgU2VyaWVzIHByb2JsZW0uIFdlIHdpbGwgbWFrZSBvdXIgcHJlZGljdGlvbnMgdXNpbmcgdGhlIGRhdGEgZnJvbSB0d28gZGlmZmVyZW50IHN5c3RlbXM6CgorIEFpclJFR0k6IEEgcmVzZXJ2YXRpb24gY29udHJvbCBhbmQgY2FzaCByZWdpc3RlciBzeXN0ZW0KKyBIb3QgUGVwcGVyIEdvdXJtZXQ6IEEgWWVscC1lcXVpdmFsZW50IHdlc2JpdGUgd2hlcmUgdXNlcnMgY2FuIGxvb2sgdXAgZm9yIHJlc3RhdXJhbnRzIGFuZCByZXNlcnZlIG9ubGluZS4KClRoZXJlIHdpbGwgYmUgYWRkaXRpb25hbCBzdG9yZSBpbmZvcm1hdGlvbiBwcm92aWRlZCwgcmVnYXJkaW5nIHRoZWlyIHNwZWNpZmljIGxvY2F0aW9ucyAobGF0LCBsb25nKSwgcHJlZmVjdHVyZSwgdGhlIGZvb2Qgb3IgY3Vpc2luZSB0aGV5IGFyZSBzZXJ2aW5nLiAKClRMO0RSOiBUaGlzIGtlcm5lbCBpcyBhIGxvbmcgZHVlIHByb2plY3QgZm9yIG1lLiBXaGVuIHRoZSBjb21wZXRpdGlvbiB3YXMgZmlyc3QgYW5ub3VuY2VkLCBJIHdhcyBzbyBleGNpdGVkIGJlY2F1c2UgdGhlIHJlc3RhdXJhbnQgaW5kdXN0cnkgd2FzIHRoZSBvbmUgdGhhdCBzdGFydGVkIG15IGpvdXJuZXkgaW4gQW5hbHl0aWNzL0RhdGEgU2NpZW5jZS4gU28gZGVzcGl0ZSB0aGUgZW5vcm1vdXMgYW1vdW50IG9mIHNjaG9vbCB3b3JrbG9hZCBJJ20gaGF2aW5nIHJpZ2h0IG5vdywgSSBtYW5hZ2VkIHRvIHNxdWVlemUgc29tZSB0aW1lIG91dCBmb3IgaXQuIEFjdHVhbGx5LCBteSBuZXh0IGV4YW0gaXMgZm9yIFRpbWUgU2VyaWVzLCBzbyBJJ20gdHJ5aW5nIG15IGJlc3QgdG8gdXNlIHdoYXQgSSd2ZSBsZWFybmVkIGZyb20gY2xhc3MhCgojIyBMb2FkaW5nIERhdGEKCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CmZpbGVzIDwtIGxpc3QuZmlsZXMocGF0aD0iLi9kYXRhLyIsIHBhdHRlcm49IiouY3N2JCIpCmZvciAoZiBpbiBmaWxlcyl7CiAgZmlsZXBhdGggPC0gZmlsZS5wYXRoKCIuL2RhdGEiLCBwYXN0ZShmKSkKICBmbmFtZSA8LSBzdHJzcGxpdChmLCAiWy5dIilbWzFdXQogIGFzc2lnbihmbmFtZVsxXSwgZnJlYWQoZmlsZXBhdGgpKQp9CmBgYAoKYGBge3IgZXZhbD1UUlVFfQphaXJfdmlzaXRfZGF0YSA8LSBmcmVhZCgnLi9kYXRhL2Fpcl92aXNpdF9kYXRhLmNzdicpCmFpcl9yZXNlcnZlIDwtIGZyZWFkKCcuL2RhdGEvYWlyX3Jlc2VydmUuY3N2JykKaHBnX3Jlc2VydmUgPC0gZnJlYWQoJy4vZGF0YS9ocGdfcmVzZXJ2ZS5jc3YnKQpkYXRlX2luZm8gPC0gZnJlYWQoJy4vZGF0YS9kYXRlX2luZm8uY3N2JykKaHBnX3N0b3JlX2luZm8gPC0gZnJlYWQoJy4vZGF0YS9ocGdfc3RvcmVfaW5mby5jc3YnKQphaXJfc3RvcmVfaW5mbyA8LSBmcmVhZCgnLi9kYXRhL2Fpcl9zdG9yZV9pbmZvLmNzdicpCnN0b3JlX2lkX3JlbGF0aW9uIDwtIGZyZWFkKCcuL2RhdGEvc3RvcmVfaWRfcmVsYXRpb24uY3N2JykjIGFkZCBkYXRlIG9mIHdlZWsKYGBgCgojIyBTaW1wbGUgRmVhdHVyZSBFbmdpbmVlcmluZwoKYGBge3IsIGVjaG89VFJVRX0KIyBzcGxpdCBkYXRlIGFuZCB0aW1lCmFpcl9yZXNlcnZlJHZpc2l0X2RhdGUgPC0gYXMuRGF0ZShhaXJfcmVzZXJ2ZSR2aXNpdF9kYXRldGltZSkKYWlyX3Jlc2VydmUkdmlzaXRfdGltZSA8LSBmb3JtYXQoc3RycHRpbWUoYWlyX3Jlc2VydmUkdmlzaXRfZGF0ZXRpbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9ybWF0PSIlWS0lbS0lZCAlSDolTTolUyIpLCAiJUg6JU0iKQoKYWlyX3Jlc2VydmUkcmVzZXJ2ZV9kYXRlIDwtIGFzLkRhdGUoYWlyX3Jlc2VydmUkcmVzZXJ2ZV9kYXRldGltZSkKYWlyX3Jlc2VydmUkcmVzZXJ2ZV90aW1lIDwtIGZvcm1hdChzdHJwdGltZShhaXJfcmVzZXJ2ZSRyZXNlcnZlX2RhdGV0aW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZvcm1hdD0iJVktJW0tJWQgJUg6JU06JVMiKSwgIiVIOiVNIikgCgpocGdfcmVzZXJ2ZSR2aXNpdF9kYXRlIDwtIGFzLkRhdGUoaHBnX3Jlc2VydmUkdmlzaXRfZGF0ZXRpbWUpCmhwZ19yZXNlcnZlJHZpc2l0X3RpbWUgPC0gZm9ybWF0KHN0cnB0aW1lKGhwZ19yZXNlcnZlJHZpc2l0X2RhdGV0aW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZvcm1hdD0iJVktJW0tJWQgJUg6JU06JVMiKSwgIiVIOiVNIikKCmhwZ19yZXNlcnZlJHJlc2VydmVfZGF0ZSA8LSBhcy5EYXRlKGhwZ19yZXNlcnZlJHJlc2VydmVfZGF0ZXRpbWUpCmhwZ19yZXNlcnZlJHJlc2VydmVfdGltZSA8LSBmb3JtYXQoc3RycHRpbWUoaHBnX3Jlc2VydmUkcmVzZXJ2ZV9kYXRldGltZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmb3JtYXQ9IiVZLSVtLSVkICVIOiVNOiVTIiksICIlSDolTSIpIAoKZGF0ZV9pbmZvJGNhbGVuZGFyX2RhdGUgPC0gYXMuRGF0ZShkYXRlX2luZm8kY2FsZW5kYXJfZGF0ZSkKYWlyX3Zpc2l0X2RhdGEkdmlzaXRfZGF0ZSA8LSBhcy5EYXRlKGFpcl92aXNpdF9kYXRhJHZpc2l0X2RhdGUpCgojIGFkZCBkYXRlIG9mIHdlZWsKYWlyX3Jlc2VydmUgPC0gYWlyX3Jlc2VydmUgJT4lIAogIG11dGF0ZSh2aXNpdF9kb3c9d2RheSh2aXNpdF9kYXRlLCBsYWJlbD1UUlVFKSkgJT4lIAogIG11dGF0ZShyZXNlcnZlX2Rvdz13ZGF5KHJlc2VydmVfZGF0ZSwgbGFiZWw9VFJVRSkpCgpocGdfcmVzZXJ2ZSA8LSBocGdfcmVzZXJ2ZSAlPiUgCiAgbXV0YXRlKHZpc2l0X2Rvdz13ZGF5KHZpc2l0X2RhdGUsIGxhYmVsPVRSVUUpKSAlPiUgCiAgbXV0YXRlKHJlc2VydmVfZG93PXdkYXkocmVzZXJ2ZV9kYXRlLCBsYWJlbD1UUlVFKSkKCmFpcl92aXNpdF9kYXRhIDwtIGFpcl92aXNpdF9kYXRhICU+JSAKICBtdXRhdGUodmlzaXRfZG93PXdkYXkodmlzaXRfZGF0ZSwgbGFiZWw9VFJVRSkpCgojIGFkZCBob2xpZGF5IGZsYWdzCmFpcl9yZXNlcnZlIDwtIGFpcl9yZXNlcnZlICU+JSBsZWZ0X2pvaW4oZGF0ZV9pbmZvLCBieT1jKCJ2aXNpdF9kYXRlIj0iY2FsZW5kYXJfZGF0ZSIpKQpocGdfcmVzZXJ2ZSA8LSBocGdfcmVzZXJ2ZSAlPiUgbGVmdF9qb2luKGRhdGVfaW5mbywgYnk9YygidmlzaXRfZGF0ZSI9ImNhbGVuZGFyX2RhdGUiKSkKYWlyX3Zpc2l0X2RhdGEgPC0gYWlyX3Zpc2l0X2RhdGEgJT4lIGxlZnRfam9pbihkYXRlX2luZm8sIGJ5PWMoInZpc2l0X2RhdGUiPSJjYWxlbmRhcl9kYXRlIikpCgphaXJfcmVzZXJ2ZSRob2xpZGF5X2ZsZyA8LSBmYWN0b3IoYWlyX3Jlc2VydmUkaG9saWRheV9mbGcsIGxhYmVscz1jKCJOb3QgSG9saWRheSIsICJIb2xpZGF5IikpCmhwZ19yZXNlcnZlJGhvbGlkYXlfZmxnIDwtIGZhY3RvcihocGdfcmVzZXJ2ZSRob2xpZGF5X2ZsZywgbGFiZWxzPWMoIk5vdCBIb2xpZGF5IiwgIkhvbGlkYXkiKSkKYWlyX3Zpc2l0X2RhdGEkaG9saWRheV9mbGcgPC0gZmFjdG9yKGFpcl92aXNpdF9kYXRhJGhvbGlkYXlfZmxnLCBsYWJlbHM9YygiTm90IEhvbGlkYXkiLCAiSG9saWRheSIpKQpgYGAKCiMjIE5vbi1UaW1lLVNlcmllcyBBbmFseXNpcyAKCiMjIyBTdG9yZSBDb3VudCAKCk92ZXJhbGwsIHdlIGhhdmUgKio4MjkqKiB1bmlxdWUgYWlyIHN0b3JlcyB0aGF0IHdlIG5lZWQgdG8gZm9yZWNhc3QgdGhlaXIgbnVtYmVyIG9mIHZpc2l0b3JzIGluIHRoZSBHb2xkZW4gV2Vlay4gQW1vbmdzdCB0aG9zZSBzdG9yZXMsIG9ubHkgKiozMTQqKiBzdG9yZXMgaGF2ZSByZXNlcnZhdGlvbiBpbmZvcm1hdGlvbi4gV2UgYXJlIGFsc28gZ2l2ZW4gdGhlIGluZm9ybWF0aW9uIGZyb20gSG90IFBlcHBlciBHb3VybWV0IHdpdGggZGF0YSBvZiBvdmVyIDEzLDAwMCBzdG9yZXMgd2hpY2ggaXMgYSBtdWNoIGdyZWF0ZXIgbnVtYmVyIG9mIG9ic2VydmF0aW9ucyBjb21wYXJpbmcgdG8gQWlyLiBZZXQsIG5vdGUgdGhhdCBub3QgYWxsICoqQWlyKiogcmVzdGF1cmFudHMgaGF2ZSBpbmZvcm1hdGlvbiBpbiB0aGUgSFBHIHN5c3RlbS4gT25seSAxNTAgb2YgdGhlbSBoYXZlIHJlc2VydmF0aW9uIGRhdGEgYW5kIDYzIG9mIHRoZW0gaGF2ZSBzdG9yZSBiYXNpYyBpbmZvcm1hdGlvbiBvbiBIUEcuIAoKYGBge3IgZWNobz1GQUxTRX0Kbl9yZXNlcnZlIDwtIGFpcl9yZXNlcnZlICU+JSBkaXN0aW5jdChhaXJfc3RvcmVfaWQpICU+JSBjb3VudCgpCnByaW50KHBhc3RlKCJhaXJfcmVzZXJ2ZSBjb250YWlucyByZXNlcnZhdGlvbiBpbmZvIG9mIiwgbl9yZXNlcnZlLCAic3RvcmVzIiwgc2VwPSIgIikpCgpuX3Zpc2l0IDwtIGFpcl92aXNpdF9kYXRhICU+JSBkaXN0aW5jdChhaXJfc3RvcmVfaWQpICU+JSBjb3VudCgpCnByaW50KHBhc3RlKCJhaXJfdmlzaXQgY29udGFpbnMgYWN0dWFsIHZpc2l0b3JzIGluZm8gb2YiLCBuX3Zpc2l0LCAic3RvcmVzIiwgc2VwPSIgIikpCgphaXJfaW5mbyA8LSBhaXJfc3RvcmVfaW5mbyAlPiUgZGlzdGluY3QoYWlyX3N0b3JlX2lkKSAlPiUgY291bnQoKQpwcmludChwYXN0ZSgiYWlyX3N0b3JlX2luZm8gY29udGFpbnMgYWN0dWFsIHZpc2l0b3JzIGluZm8gb2YiLCBhaXJfaW5mbywgInN0b3JlcyIsIHNlcD0iICIpKQoKaHBnX25fcmVzZXJ2ZSA8LWhwZ19yZXNlcnZlICU+JSBkaXN0aW5jdChocGdfc3RvcmVfaWQpICU+JSBjb3VudCgpCmhwZ19haXJfcmVzZXJ2ZSA8LSBhaXJfc3RvcmVfaW5mbyAlPiUgCiAgbGVmdF9qb2luKHN0b3JlX2lkX3JlbGF0aW9uLCBjKCJhaXJfc3RvcmVfaWQiKSkgJT4lIAogIGxlZnRfam9pbihocGdfcmVzZXJ2ZSwgYygiaHBnX3N0b3JlX2lkIikpICU+JSBuYS5vbWl0KCkgJT4lIApkaXN0aW5jdChocGdfc3RvcmVfaWQpICU+JSBjb3VudCgpCgpwcmludChwYXN0ZSgiaHBnX3Jlc2VydmUgY29udGFpbnMgcmVzZXJ2YXRpb24gaW5mbyBvZiIsIGhwZ19uX3Jlc2VydmUsICJzdG9yZXMgYnV0IG9ubHkiLGhwZ19haXJfcmVzZXJ2ZSwgImJlbG9uZ3MgdG8gdGhlIEFpclJFR0kgc3lzdGVtIiwgc2VwPSIgIikpCgpocGdfaW5mbyA8LSBocGdfc3RvcmVfaW5mbyAlPiUgZGlzdGluY3QoaHBnX3N0b3JlX2lkKSAlPiUgY291bnQoKQpocGdfYWlyX2luZm8gPC0gYWlyX3N0b3JlX2luZm8gJT4lIAogIGxlZnRfam9pbihzdG9yZV9pZF9yZWxhdGlvbiwgYygiYWlyX3N0b3JlX2lkIikpICU+JSAKICBsZWZ0X2pvaW4oaHBnX3N0b3JlX2luZm8sIGMoImhwZ19zdG9yZV9pZCIpKSAlPiUgbmEub21pdCgpICU+JSAKZGlzdGluY3QoaHBnX3N0b3JlX2lkKSAlPiUgY291bnQoKQoKcHJpbnQocGFzdGUoImhwZ19zdG9yZV9pbmZvIGNvbnRhaW5zIHJlc2VydmF0aW9uIGluZm8gb2YiLCBocGdfaW5mbywgInN0b3JlcyBidXQgb25seSIsaHBnX2Fpcl9pbmZvLCAiYmVsb25ncyB0byB0aGUgQWlyUkVHSSBzeXN0ZW0iLCBzZXA9IiAiKSkKYGBgCgojIyMgU3RvcmUgQ2F0ZWdvcmllcyAKClRoZSB0d28gZGF0YXNldCBhbHNvIGhhdmUgcmVzdGF1cmFudCBnZW5yZXMgYWxiZWl0IHRoZXkgYXJlIGNhdGVnb3JpemVkIGRpZmZlcmVudGx5LiBMZXQncyBjb21wYXJlIHRoZSBhY3R1YWwgbnVtYmVyIG9mIHZpc2l0b3JzIHRvIHRoZSBBaXIgc3RvcmVzIGJhc2VkIG9uIHRoZSBjYXRlZ29yaWVzIG9mIEFpciB2cy4gSFBHLiBJemFrYXlhICh3aGljaCBpcyBhbiBhbWF6aW5nIEphcGFuZXNlIGRpbmluZyBleHBlcmllbmNlIHdoZXJlIHlvdSBhcmUgc2VydmVkIHdpdGggc21hbGwgZGlzaGVzIHdoaWxlIGVuam95aW5nIHRoZSBkcmlua3Mgd2l0aCB5b3VyIGZyaWVuZHMpIGlzIHRoZSBtb3N0IHBvcHVsYXIgZm9yIHRoZSBBaXJSRUdJIHN5c3RlbSAtLSB3aGljaCBhbGlnbnMgd2l0aCB0aGUgIzEgc3BvdCBvbiB0aGUgSFBHIHJhbmtzIGFzIHdlbGwuCgpIb3dldmVyLCB3aGlsZSBjYWZlL3N3ZWV0cyBob2xkIHRoZSBzZWNvbmQgcGxhY2UgaW4gQWlyLCBpdCBvbmx5IGhhcyBhIGZldyBudW1iZXIgb2YgdmlzaXRvcnMgYmFzZWQgb24gdGhlIGNhdGVnb3J5IG9mIEhQRy4gVGhpcyBzdWdnZXN0cyB0aGUgY2F0ZWdvcmllcyBvZiB0aGUgdHdvIHN5c3RlbXMgbWF5IG5vdCBiZSBpbiBzeW5jIGFzIHdlIGhvcGUgdGhleSB3b3VsZCBiZS4gCgpgYGB7ciBmaWcud2lkdGg9MTUsIGZpZy5oZWlnaHQ9NywgZWNobz1GQUxTRX0KcDEgPC0gYWlyX3N0b3JlX2luZm8gJT4lIAogIGxlZnRfam9pbihhaXJfdmlzaXRfZGF0YSwgYygiYWlyX3N0b3JlX2lkIikpICU+JQogIGRwbHlyOjpncm91cF9ieShhaXJfZ2VucmVfbmFtZSkgJT4lIAogIGRwbHlyOjpzdW1tYXJpemUoYWN0X3Zpc2l0b3JfdG9sID0gc3VtKHZpc2l0b3JzKSkgJT4lCiAgZ2dwbG90KCkgKwogIGdlb21fYmFyKGFlcyh4PXJlb3JkZXIoYWlyX2dlbnJlX25hbWUsYWN0X3Zpc2l0b3JfdG9sKSwKICAgICAgICAgICAgICAgICAgICAgICAgIHk9YWN0X3Zpc2l0b3JfdG9sKSwgc3RhdD0naWRlbnRpdHknLCBmaWxsPScjMUE1ODg3JykgKwogIGxhYnMoeD0iYWlyX2dlbnJlIiwgdGl0bGU9IlZpc3RvcnMgYnkgQWlyIFJlc3RhdXJhbnQgR2VucmUiKSArCiAgZ3VpZGVzKGZpbGw9RkFMU0UpICsKICBjb29yZF9mbGlwKCkgKwogIHRoZW1lKGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xNCksIAogICAgICAgICAgICAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChzaXplPTE0KSwKICAgICAgICAgICAgICAgICBheGlzLnRleHQueT1lbGVtZW50X3RleHQoc2l6ZT0xNCksCiAgICAgICAgICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTIwKSwKICAgICAgICAgICAgICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTcpKQogIApwMiA8LSBhaXJfc3RvcmVfaW5mbyAlPiUgCiAgbGVmdF9qb2luKGFpcl92aXNpdF9kYXRhLCBjKCJhaXJfc3RvcmVfaWQiKSkgJT4lCiAgbGVmdF9qb2luKHN0b3JlX2lkX3JlbGF0aW9uLCBjKCJhaXJfc3RvcmVfaWQiKSkgJT4lIAogIGxlZnRfam9pbihocGdfc3RvcmVfaW5mbywgYygiaHBnX3N0b3JlX2lkIikpICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoaHBnX2dlbnJlX25hbWUpICU+JSAKICBkcGx5cjo6c3VtbWFyaXplKGFjdF92aXNpdG9yX3RvbCA9IHN1bSh2aXNpdG9ycykpICU+JQogIG5hLm9taXQoKSAlPiUgCiAgZ2dwbG90KCkgKwogIGdlb21fYmFyKGFlcyh4PXJlb3JkZXIoaHBnX2dlbnJlX25hbWUsIGFjdF92aXNpdG9yX3RvbCksCiAgICAgICAgICAgICAgICAgICAgICAgICB5PWFjdF92aXNpdG9yX3RvbCksIHN0YXQ9J2lkZW50aXR5JyxmaWxsPSIjQzcwNTA5IikgKwogIGxhYnMoeD0iaHBnX2dlbnJlIiwgdGl0bGU9IlZpc3RvcnMgYnkgSFBHIFJlc3RhdXJhbnQgR2VucmUiKSArCiAgZ3VpZGVzKGZpbGw9RkFMU0UpICsKICBjb29yZF9mbGlwKCkgICsKICB0aGVtZShheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTQpLCAKICAgICAgICAgICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoc2l6ZT0xNCksCiAgICAgICAgICAgICAgICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF90ZXh0KHNpemU9MTQpLAogICAgICAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0yMCksCiAgICAgICAgICAgICAgICBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTE3KSkgCgpncmlkLmFycmFuZ2UocDEscDIsbmNvbD0yKQpgYGAKCiMjIENvcnJlbGF0aW9uIEJldHdlZW4gUmVzdGF1cmFudCBDYXRlZ29yaWVzCgpgYGB7ciBldmFsPUZBTFNFLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9NSwgb3V0LndpZHRoPSIxMDAlIn0KZGYxIDwtIGFpcl9zdG9yZV9pbmZvICU+JSAKICBsZWZ0X2pvaW4oYWlyX3Zpc2l0X2RhdGEsIGMoImFpcl9zdG9yZV9pZCIpKSAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KGFpcl9nZW5yZV9uYW1lLCB2aXNpdF9kYXRlKSAlPiUgCiAgZHBseXI6OnN1bW1hcml6ZShhY3RfdmlzaXRvcl90b2wgPSBzdW0odmlzaXRvcnMpKSAlPiUgCiAgZGNhc3QodmlzaXRfZGF0ZSB+IGFpcl9nZW5yZV9uYW1lKSAlPiUgCiAgc2VsZWN0KC1jKEFzaWFuKSkgJT4lICMgbW9yZSB0aGFuIGhhbGYgYXJlIE5BIGVudHJpZXMKICBzZWxlY3QoLWNvbnRhaW5zKCJLYXJhb2tlL1BhcnR5IikpICU+JSAjIG1vcmUgdGhhbiBoYWxmIGFyZSBOQSBlbnRyaWVzCiAgc2VsZWN0KC1jb250YWlucygiSW50ZXJuYXRpb25hbCBjdWlzaW5lIikpICMgbW9yZSB0aGFuIGhhbGYgYXJlIE5BIGVudHJpZXMKCiMgaW1wdXRlIG1pc3NpbmcgdmFsdWVzCmRmMSA8LSBuYS5zZWFkZWMoZGYxLCBhbGdvcml0aG09ImludGVycG9sYXRpb24iKQp0c2Rpc3QgPC0gdChzZWxlY3QoZGYxLCAtMSkpICU+JSBzY2FsZSgpICU+JSBkaXNzKCJBQ0YiLCBwPTAuMDUpCmhjIDwtIGhjbHVzdCh0c2Rpc3QpCgpkZW5kIDwtIGFzLmRlbmRyb2dyYW0oaGMpCmRlbmQgPC0gZGVuZCAlPiUKICBkZW5kZXh0ZW5kOjpzZXQoImJyYW5jaGVzX2tfY29sb3IiLCBrPTQpICU+JSAKICBkZW5kZXh0ZW5kOjpzZXQoImJyYW5jaGVzX2x3ZCIsIDEuMCkgJT4lCiAgZGVuZGV4dGVuZDo6c2V0KCJsYWJlbHNfY2V4IiwgMS4yKSAlPiUgCiAgZGVuZGV4dGVuZDo6c2V0KCJsZWF2ZXNfcGNoIiwgMTkpICU+JSAgCiAgZGVuZGV4dGVuZDo6c2V0KCJsZWF2ZXNfY29sIiwgYygiYmxhY2siKSkgJT4lCiAgZGVuZGV4dGVuZDo6c2V0KCJsZWF2ZXNfY2V4IiwgMC42KSAlPiUgCiAgaGlnaGxpZ2h0X2JyYW5jaGVzX2NvbAoKcGFyKGNleD0wLjQpCmdncGxvdChkZW5kLCBob3Jpej1UUlVFKSAKYGBgCgoKIyMjIFN0b3JlIExvY2F0aW9ucwoKTWF5YmUgaXQncyBhIGdvb2QgdGltZSB0byBsZWFybiBhIGxpdHRsZSBiaXQgYWJvdXQgdGhlIEphcGFuZXNlIGdlb2dyYXBoeSEgRmlyc3QsIHRoZXJlIGFyZSA0NyBkaWZmZXJlbnQgKmtlbiogKOecjCkgb3IgcHJlZmVjdHVyZXMsIHdoaWNoIGlzIHRoZSBtb3N0IGhpZ2hlc3QgbGV2ZWwgZ2VvZ3JhcGhpYyBkaXZpc2lvbiBvZiBKYXBhbi4gSXRzIGNsb3Nlc3QgYW5hbG9neSBpcyB0aGUgc3RhdGUgb2YgdGhlIFVuaXRlZCBTdGF0ZXMuIFRocmVlIG90aGVyIHNwZWNpYWwgcHJlZmVjdHVyZSBkZXNpZ25hdGlvbnMgYXJlICp0byogKOmDvSkgZm9yIFRva3lvLCAqZMWNKiAo6YGTKSBmb3IgSG9ra2FpZMWNIGFuZCAqZnUqICjlupwpIGZvciBPc2FrYSBhbmQgS3lvdG8uIAoKRm9sbG93aW5nIHRoZSBwcmVmZWN0dXJlLCB3ZSBoYXZlICpzaGkqICjluIIpIGZvciBjaXRpZXMgYW5kIHRoZW4sICprdSogKOWMuikgb3Igd2FyZHMuIEZvciBsYXJnZXIgY2l0aWVzIHN1Y2ggYXMgVG9reW8gb3IgS3lvdG8sIHRoZWlyIGFkZHJlc3NlcyBkbyBub3QgZm9sbG93IHRoZSBhYm92ZSBoaWVyYWNoeS4gCgoKYGBge3IgZWNobz1GQUxTRX0KIyBBSVIgLS0gc3BsaXQgbG9jYXRpb24gZGF0YQphaXJfc3RvcmVfaW5mbyA8LSBhaXJfc3RvcmVfaW5mbyAlPiUgCiAgc2VwYXJhdGUoYWlyX2FyZWFfbmFtZSwgc2VwPSIgIiwgaW50bz1jKCdwcmVmZWN0dXJlJywgJ3N1Yi1kaXZpc2lvbi0xJywgJ3N1Yi1kaXZpc2lvbi0yJyksIGV4dHJhPSJtZXJnZSIpIAoKIyBzdG9yZSB2aXNpdCBjb3VudAphaXIxIDwtIGFpcl92aXNpdF9kYXRhICU+JSBncm91cF9ieShhaXJfc3RvcmVfaWQpICU+JSBzdW1tYXJpemUoYWN0X3Zpc2l0b3JfdG9sID0gc3VtKHZpc2l0b3JzKSkKYWlyMiA8LSBhaXJfcmVzZXJ2ZSAlPiUgZ3JvdXBfYnkoYWlyX3N0b3JlX2lkKSAlPiUgc3VtbWFyaXplKHJlc2VydmVfdmlzaXRvcl90b2wgPSBzdW0ocmVzZXJ2ZV92aXNpdG9ycykpCgphaXJfc3RvcmVfc3VtbSA8LSBhaXJfc3RvcmVfaW5mbyAlPiUgCiAgbGVmdF9qb2luKGFpcjEsIGMoImFpcl9zdG9yZV9pZCIpKSAlPiUKICBsZWZ0X2pvaW4oYWlyMiwgYygiYWlyX3N0b3JlX2lkIikpICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkocHJlZmVjdHVyZSkgJT4lIAogIHN1bW1hcml6ZShhY3R1YWxfdmlzaXRvcl90b2wgPSBtZWFuKGFjdF92aXNpdG9yX3RvbCwgbmEucm09VCksIAogICAgICAgICAgICByZXNlcnZlX3Zpc2l0b3JfdG9sID0gbWVhbihyZXNlcnZlX3Zpc2l0b3JfdG9sLCBuYS5ybT1UKSwgCiAgICAgICAgICAgIGFpcl9zdG9yZV9jb3VudCA9IG4oKSkKCiMgSFBHIC0tIHNwbGl0IGxvY2F0aW9uIGRhdGEKaHBnX3N0b3JlX3N1bW0gPC0gaHBnX3N0b3JlX2luZm8gJT4lIAogIHNlcGFyYXRlKGhwZ19hcmVhX25hbWUsIHNlcD0iICIsIGludG89YygncHJlZmVjdHVyZScsICdzdWItZGl2aXNpb24tMScsICdzdWItZGl2aXNpb24tMicpLCBleHRyYT0ibWVyZ2UiKSAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KHByZWZlY3R1cmUpICU+JSAKICBzdW1tYXJpemUoaHBnX3N0b3JlX2NvdW50ID0gbigpKQoKIyByZW5hbWUgCmhwZ19zdG9yZV9zdW1tWydwcmVmZWN0dXJlJ10gPC0gYygiRnVrdW9rYSIsICJIaXJvc2hpbWEiLCAiSG9ra2FpZG8iLCAiSHlvZ28iLCAiS2FuYWdhd2EiLCAiTWl5YWdpIiwgIk5paWdhdGEiLCAiTm9uZSIsICJPc2FrYSIsICJPc2FrYSIsICJTYWl0YW1hIiwgIlNoaXp1b2thIiwgIlRva3lvIikKCmhwZ19zdG9yZV9zdW1tIDwtIGhwZ19zdG9yZV9zdW1tICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkocHJlZmVjdHVyZSkgJT4lCiAgc3VtbWFyaXplKGhwZ19zdG9yZV9jb3VudCA9IHN1bShocGdfc3RvcmVfY291bnQpKQpgYGAKCgpgYGB7cn0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgUmVmZXJlbmNlIENvZGU6IGh0dHBzOi8vd3d3LmthZ2dsZS5jb20vY2FwdGNhbGN1bGF0b3IvYS12ZXJ5LWV4dGVuc2l2ZS1yZWNydWl0LWV4cGxvcmF0b3J5LWFuYWx5c2lzIAojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCiMgY3JlYXRlIHNwIG9iamVjdApqbWFwIDwtIG1hcCgiamFwYW4iLCBjb2w9MSwgcGxvdD1GQUxTRSwgZmlsbD1UUlVFKQpqbWFwX2lkIDwtc2FwcGx5KHN0cnNwbGl0KGptYXBbWyduYW1lcyddXSwgIjoiKSwgZnVuY3Rpb24oeCkgeFsxXSkKcDRzIDwtIENSUygiK3Byb2o9bG9uZ2xhdCArZGF0dW09V0dTODQiKQpqc3AgPC0gbWFwMlNwYXRpYWxQb2x5Z29ucyhqbWFwLCBJRD1qbWFwX2lkLCBwcm9qNHN0cmluZz1wNHMpCgojIGNyZWF0ZSBTcGF0aWFsUG9seWdvbnNEYXRhRnJhbWUKanNwZGYgPC0gZGF0YS5mcmFtZShwcmVmZWN0dXJlPW5hbWVzKGpzcCkpCnJvd25hbWVzKGpzcGRmKSA8LSBuYW1lcyhqc3ApCmpzcGRmIDwtIFNwYXRpYWxQb2x5Z29uc0RhdGFGcmFtZShqc3AsIGpzcGRmKQoKIyByZW5hbWUgY29sdW1ucwphaXJfc3RvcmVfc3VtbVsncHJlZmVjdHVyZSddIDwtIGMoIkZ1a3Vva2EiLCAiSGlyb3NoaW1hIiwgIkhva2thaWRvIiwgIkh5b2dvIiwgIk1peWFnaSIsICJOaWlnYXRhIiwgIk9zYWthIiwgIlNoaXp1b2thIiwgIlRva3lvIikKCmpzcGRmIDwtIHNwOjptZXJnZShqc3BkZiwgYWlyX3N0b3JlX3N1bW0sIGJ5PWMoInByZWZlY3R1cmUiKSkKanNwZGYgPC0gc3A6Om1lcmdlKGpzcGRmLCBocGdfc3RvcmVfc3VtbVtocGdfc3RvcmVfc3VtbSE9Ik5vbmUiLF0sIGJ5PWMoInByZWZlY3R1cmUiKSkKYGBgCgoKYGBge3IgZWNobz1GQUxTRSwgb3V0LndpZHRoPSIxMDAlIn0KIyBjcmVhdGUgdGhlIGNob3JvcGxldGggcGFsZXR0ZQpiaW5zIDwtIGMoNTUwMCwgNjAwMCwgNjUwMCwgNzAwMCwgNzUwMCwgODAwMCwgSW5mKQpwYWwgPC0gY29sb3JCaW4oIkduQnUiLCBkb21haW4gPSBqc3BkZiRzdG9yZV9jb3VudCwgYmlucyA9IGJpbnMpCgojIGNyZWF0ZSB0aGUgcHJlZmVjdHVyZSBsYWJlbHMKbGFiZWxzIDwtIHNwcmludGYoCiAgICAiPHN0cm9uZz4lczwvc3Ryb25nPjwvYnI+QXZlcmFnZSBWaXNpdG9yIHBlciBTdG9yZTogJS4wZjxicj4jIEFpciBTdG9yZXM6ICUuMGY8YnI+IyBIUEcgU3RvcmVzOiAlLjBmIiwKICAgIGpzcGRmJHByZWZlY3R1cmUsIGpzcGRmJGFjdHVhbF92aXNpdG9yX3RvbCwganNwZGYkYWlyX3N0b3JlX2NvdW50LCBqc3BkZiRocGdfc3RvcmVfY291bnQpICU+JSAKICBsYXBwbHkoaHRtbHRvb2xzOjpIVE1MKQoKCmxlYWZsZXQoanNwZGYpICU+JQogIHNldFZpZXcoMTM4LjA4NiwgMzguNzQ1LCA1KSAlPiUgCiAgYWRkUHJvdmlkZXJUaWxlcygiQ2FydG9EQi5Qb3NpdHJvbiIpICU+JSAKICBhZGRQb2x5Z29ucyhkYXRhID0ganNwZGYsIHdlaWdodCA9IDEsIHNtb290aEZhY3RvciA9IDAuMiwKICAgICAgICAgICAgICBmaWxsQ29sb3IgPSB+cGFsKGpzcGRmJGFjdHVhbF92aXNpdG9yX3RvbCksCiAgICAgICAgICAgICAgb3BhY2l0eSA9IDEsIGNvbG9yID0gIndoaXRlIiwgZGFzaEFycmF5ID0gIjIiLCBmaWxsT3BhY2l0eSA9IDAuOCwKICAgICAgICAgICAgICBoaWdobGlnaHQgPSBoaWdobGlnaHRPcHRpb25zKAogICAgICAgICAgICAgICAgICB3ZWlnaHQgPSAxLCBjb2xvciA9ICIjRENEQ0RDIiwgZGFzaEFycmF5ID0gIiIsIGZpbGxPcGFjaXR5ID0gMC44LAogICAgICAgICAgICAgICAgICBicmluZ1RvRnJvbnQgPSBUUlVFKSwKICAgICAgICAgICAgICBsYWJlbCA9IGxhYmVscykgJT4lIAogIGFkZExlZ2VuZChwYWw9cGFsLCB2YWx1ZXM9fmFjdHVhbF92aXNpdG9yX3RvbCwgCiAgICAgICAgICAgIG9wYWNpdHkgPSAwLjcsIHRpdGxlID0iQXZlcmFnZSBWaXNpdG9ycyBwZXIgU3RvcmUiLCBwb3NpdGlvbiA9ICJib3R0b21yaWdodCIpCmBgYAoKXCAgCgpJbiB0aGUgZGF0YSwgd2UncmUgZ2l2ZW4gdGhlIHRocmVlIGNvbXBvbmVudHMgb2YgYSByZXN0YXVyYW50J3MgYWRkcmVzcywgc28gbGV0J3Mgc3BsaXQgdGhlbSBvdXQgc28gd2UgY291bGQgc2VlIGEgaGlnaC1sZXZlbCBzdW1tYXJ5IG9mIHRoZSBzdG9yZSBsb2NhdGlvbnMuIFVuc3VycHJpc2luZ2x5LCBUb2t5byBoYXMgdGhlIG1vc3Qgc3RvcmVzIGFjcm9zcyB0aGUgY291bnRyeSwgYnV0IG9uIGF2ZXJhZ2UsIGl0IGhhcyBvbmUgb2YgdGhlIGxvd2VzdCBudW1iZXIgb2YgdmlzaXRvcnMgcGVyIHN0b3JlLiAgCgpgYGB7ciwgZWNobz1GQUxTRSwgZmlnLndpZHRoPTE1LCBmaWcuaGVpZ2h0PTcsIG91dC53aWR0aD0iMTAwJSJ9CmNvbGRfcGFsZXR0ZSA8LSBjKCcjMDAwMDAwJywgJyMwODFGMzknLCAnIzBGM0I2QycsICcjMzQ1QTc4JywgJyMzMTYyODMnLCAnIzRBODc5QycsICcjMkY4RkE4JywgJyM4OEIyQkUnLCAnI0M2RDRERCcsICcjQ0FENkRGJywgJyNFRkVGRUYnKQoKIyBwbG90IHN0b3JlIGNvdW50CnAxIDwtIGFpcl9zdG9yZV9zdW1tICU+JSAKICBnZ3Bsb3QoKSArCiAgZ2VvbV9iYXIoYWVzKHg9cmVvcmRlcihwcmVmZWN0dXJlLCBhaXJfc3RvcmVfY291bnQpLCB5PWFpcl9zdG9yZV9jb3VudCwgZmlsbD1wcmVmZWN0dXJlKSwgc3RhdD0naWRlbnRpdHknLCBwb3NpdGlvbj0iZG9kZ2UiKSArCiAgbGFicyh0aXRsZT0iQWlyIFN0b3JlIENvdW50IGJ5IFByZWZlY3R1cmUiLCB5PSJzdG9yZV9jb3VudCIgLCB4PSJwcmVmZWN0dXJlIikgKwogIGd1aWRlcyhmaWxsPUZBTFNFKSArCiAgdGhlbWUoYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTE0KSwKICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoc2l6ZT0xNCwgYW5nbGU9OTAsIGhqdXN0PTEpLAogICAgICAgIGF4aXMudGV4dC55PWVsZW1lbnRfdGV4dChzaXplPTE0KSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MjApLAogICAgICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTcpKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWNvbGRfcGFsZXR0ZSkKCiMgcGxvdCBzdG9yZSB2aXNpdCBjb3VudApwMiA8LSBhaXJfc3RvcmVfc3VtbSAgJT4lCiAgZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoYWVzKHg9cmVzZXJ2ZV92aXNpdG9yX3RvbCwgeT1hY3R1YWxfdmlzaXRvcl90b2wsCiAgICAgICAgICAgICAgICAgY29sb3I9cHJlZmVjdHVyZSwgc2l6ZT1hY3R1YWxfdmlzaXRvcl90b2wpKSArCiAgbGFicyh0aXRsZT0iVmlzdG9ycyBieSBBaXIgUmVzdGF1cmFudCBHZW5yZSIpICsgIAogIHNjYWxlX3NpemVfY29udGludW91cyhyYW5nZSA9IGMoMywgMTApKSArCiAgZ3VpZGVzKGZpbGw9RkFMU0UpICsKICBjb29yZF9mbGlwKCkgKwogIHRoZW1lKGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xNCksCiAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KHNpemU9MTQpLAogICAgICAgIGF4aXMudGV4dC55PWVsZW1lbnRfdGV4dChzaXplPTE0KSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MjApLAogICAgICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTcpLCAKICAgICAgICBsZWdlbmQudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTQpLCBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xNCkpKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9Y29sZF9wYWxldHRlKQogIApncmlkLmFycmFuZ2UocDEscDIsbmNvbD0yKQpgYGAKCgojIyBGaXJzdCBMb29rIGF0IHRoZSBUaW1lIFNlcmllcwoKTGV0J3MgZmlyc3QgbG9vayBhdCB0aGUgdGltZSBzZXJpZXMgZm9yIHRoZSBudW1iZXIgb2YgdmlzaXRvcnMgZnJvbSB0aGUgQWlyUkVHSSBzeXN0ZW0uIFRoZXJlIGFyZSBzb21lIHJlcGVhdGVkIHBhdHRlcm5zIHdlIGNvdWxkIHNlZSBoZXJlIChmcmVxdWVudCBzcGlsZXMgdGhhdCByZXBlYXRlZCBldmVyeSA1IGRheXMpIC0tIHdoaWNoIGNvdWxkIGhhdmUgYmVlbiBkdWUgdG8gdGhlIGhlYXZ5IHRyYWZmaWMgb2Ygd2Vla2VuZC4gSW4gYWRkaXRpb24sIHRoZXJlIGlzIGEgc3VkZGVuIGluY3JlYXNlIGluIHRoZSBudW1iZXIgb2YgdmlzaXRvcnMgc3RhcnRpbmcgYXJvdW5kIEp1bHkgMXN0IDIwMTcuIFVzdWFsbHkgc3VjaCBsYXJnZSBzaGlmdCBpbiBkYXRhIHZhbHVlcyBpcyBkdWUgdG8gYSBzeXN0ZW1hdGljIGNoYW5nZS4gSXQgY291bGQgYmUgYmVjYXVzZSBvZiBBaXJSRUdJIGludGVybmFsIHJlcG9ydGluZyBzeXN0ZW0gb3IgbWF5YmUsIHRoZXkgbWVyZ2VkIHdpdGggYSBsYXJnZSBncm91cCBvZiByZXN0YXVyYW50cy4gV2UgYXJlIHVuc3VyZSBvZiB0aGUgY2F1c2UgZm9yIHN1Y2ggY2hhbmdlIGJ1dCBpdCBzZWVtcyBsaWtlIHdlIHdpbGwgaGF2ZSB0byBoYW5kbGUgc29tZSBtaXNzaW5nIGRhdGEgaW4gdGhpcyBjYXNlLgoKXCAgCgpgYGB7ciwgZmlnLndpZHRoPSAxMiwgZmlnLmhlaWdodD02LCBvdXQud2lkdGg9IjEwMCUifQojIEFpciBSZXNlcnZlZCBWaXNpdG9ycwphaXIxIDwtIGFpcl9yZXNlcnZlICU+JQogIGdyb3VwX2J5KHZpc2l0X2RhdGUpICU+JSBzdW1tYXJpc2UocmVzZXJ2ZV92aXNpdF90b2wgPSBzdW0ocmVzZXJ2ZV92aXNpdG9ycykpCmFpcjIgPC0gYWlyX3Zpc2l0X2RhdGEgJT4lIGdyb3VwX2J5KHZpc2l0X2RhdGUpICU+JSBzdW1tYXJpc2UoYWN0X3Zpc2l0X3RvbCA9IHN1bSh2aXNpdG9ycykpCgpyZWN0IDwtIGRhdGEuZnJhbWUoeG1pbj1hcy5EYXRlKCIyMDE2LTA3LTAxIiwgIiVZLSVtLSVkIiksCiAgICAgICAgICAgICAgICAgICB4bWF4PWFzLkRhdGUoIjIwMTYtMDctMTAiLCAiJVktJW0tJWQiKSwgeW1pbj0tSW5mLCB5bWF4PUluZikKcDEgPC0gYWlyX3Zpc2l0X2RhdGEgJT4lCiAgZ3JvdXBfYnkodmlzaXRfZGF0ZSkgJT4lIHN1bW1hcmlzZShhY3RfdmlzaXRvcl90b2wgPSBzdW0odmlzaXRvcnMpKSAgJT4lIAogIGdncGxvdCgpICsKICBnZW9tX2xpbmUoYWVzKHg9dmlzaXRfZGF0ZSwgeT1hY3RfdmlzaXRvcl90b2wpLCBzaXplPTAuMiwgY29sPSdncmV5MjUnKSArCiAgbGFicyh0aXRsZT0iQWN0dWFsIFZpc3Rpb3JzIC0gQWlyUkVHSSIpICsgCiAgc2NhbGVfeF9kYXRlKGRhdGVfYnJlYWtzID0gIjEgbW9udGgiKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTkwLCBoanVzdD0xLCBzaXplPTcpKSArCiAgZ2VvbV9yZWN0KGRhdGE9cmVjdCwgYWVzKHhtaW49eG1pbiwgeG1heD14bWF4LCB5bWluPXltaW4sIHltYXg9eW1heCksCiAgICAgICAgICAgICAgY29sb3VyPSJibHVlIiwgZmlsbD0iYmx1ZSIsIAogICAgICAgICAgICAgIGFscGhhPTAuMDUsIHNpemU9MC41LCBsaW5ldHlwZT0yLCBpbmhlcml0LmFlcyA9IEZBTFNFKQoKcmVjdCA8LSBkYXRhLmZyYW1lKHhtaW49YXMuRGF0ZSgiMjAxNi0wOC0wMSIsICIlWS0lbS0lZCIpLAogICAgICAgICAgICAgICAgICAgeG1heD1hcy5EYXRlKCIyMDE2LTEwLTIwIiwgIiVZLSVtLSVkIiksIHltaW49LUluZiwgeW1heD1JbmYpCnAyIDwtIGFpcjEgJT4lIAogIGdncGxvdCgpICsKICBnZW9tX2xpbmUoYWVzKHg9dmlzaXRfZGF0ZSwgeT1yZXNlcnZlX3Zpc2l0X3RvbCksIHNpemU9MC4yLCBjb2w9J3N0ZWVsYmx1ZScpICsKICBnZW9tX3JlY3QoZGF0YT1yZWN0LCBhZXMoeG1pbj14bWluLCB4bWF4PXhtYXgsIHltaW49eW1pbiwgeW1heD15bWF4KSwKICAgICAgICAgICAgICBjb2xvdXI9ImJsdWUiLCBmaWxsPSJibHVlIiwgCiAgICAgICAgICAgICAgYWxwaGE9MC4wNSwgc2l6ZT0wLjUsIGxpbmV0eXBlPTIsIGluaGVyaXQuYWVzID0gRkFMU0UpICsgCiAgbGFicyh0aXRsZT0iQWN0dWFsICYgUmVzZXJ2ZWQgVmlzdGlvcnMgLSBBaXJSRUdJIikgKyAKICBzY2FsZV94X2RhdGUoZGF0ZV9icmVha3MgPSAiMSBtb250aCIpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9OTAsIGhqdXN0PTEpKQoKcCA8LSBzdWJwbG90KHAxLCBwMiwgbnJvd3MgPSAyLCBzaGFyZVg9VFJVRSkKZ2dwbG90bHkocCkKYGBgCgpcICAKU3VjaCBhYm5vcm1hbCBiZWhhdmlvciBjYW4gYWxzbyBiZSBzZWVuIGluIHRoZSBudW1iZXIgb2YgdmlzaXRvcnMgcmVzZXJ2ZWQgb24gQWlyUkVHSS4gVGhlIG1vbnRoIG9mIEF1Z3VzdCBhbmQgU2VwdGVtYmVyIHdhcyB0ZWNobmljYWxseSAiZGVhZCIgd2l0aCBsaXR0bGUgdG8gbm9uZSByZXNlcnZhdGlvbnMgYmVmb3JlIGl0IHBpY2tlZCB1cCBhZ2FpbiBpbiBOb3ZlbWJlci4gVGhlIHZvbHVtZSBvZiByZXNlcnZlZCB2aXNpdG9ycyBwb3N0LU5vdmVtYmVyIGlzIGFsc28gYXQgbXVjaCBsYXJnZXIgb3JkZXIgb2YgbWFnbml0dWRlIHZlcnN1cyBwcmUtQXVndXN0IDIwMTYuIAoKSW4gYWRkaXRpb24sIG5vdGljZSB0aGF0IHRoZSBwcm9wb3J0aW9uIG9mIHJlc2VydmF0aW9ucyB0byB0aGUgYWN0dWFsIHZpc2l0cyBpcyBwcmV0dHkgbG93LCBiZXR3ZWVuIDEwLTMwJSBvbiBhdmVyYWdlIChleGNsdWRpbmcgdGhlIHRpbWUgZ2FwIGluIEF1Z3VzdCkuClwgIAoKYGBge3IgZWNobz1GQUxTRSwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTMuNSwgb3V0LndpZHRoPSIxMDAlIn0KcCA8LSBhaXIxICU+JSBsZWZ0X2pvaW4oYWlyMikgJT4lIAogIGRwbHlyOjpncm91cF9ieSh2aXNpdF9kYXRlKSAlPiUgCiAgc3VtbWFyaXNlKHByb3AgPSByZXNlcnZlX3Zpc2l0X3RvbC9hY3RfdmlzaXRfdG9sKSAlPiUgCiAgZ2dwbG90KCkgKwogIGdlb21fbGluZShhZXMoeD12aXNpdF9kYXRlLCB5PXByb3ApLCBzaXplPTAuMiwgY29sPSdncmV5MjUnKSArCiAgbGFicyh0aXRsZT0iUmF0aW8gb2YgQWlyUkVHSSBSZXNlcnZlZCBWaXNpdG9ycyB0byBBY3R1YWwgVmlzaXRvcnMiKSArIAogIHNjYWxlX3hfZGF0ZShkYXRlX2JyZWFrcyA9ICIxIG1vbnRoIikgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT05MCwgaGp1c3Q9MSkpCmdncGxvdGx5KHApCmBgYAoKCkJlbG93IGlzIHRoZSBIUEcgcmVzZXJ2ZSBwYXR0ZXJuLiBIZXJlaW4sIHRoZSB0cmFmZmljIGZvciBBdWd1c3QgYW5kIFNlcHRlbWJlciBpcyBub3QgbXVjaCBkaWZmZXJlbnQgYW5kIGV4Y2x1ZGluZyB0aGUgQ2hyaXN0bWFzLU5ZRSBzcGlrZXMsIHRoZXJlIGlzIG5vIHNpZ25pZmljYW50IHNoaWZ0IGxpa2UgdGhlIG9uZSB3ZSBoYXZlIHNlZW4gaW4gQWlyUkVHSS4gSXQgY291bGQgYmUgZGVkdWNlZCBub3cgdGhhdCBpdCdzIGFuIGludGVybmFsIGZhY3RvciB0aGF0IGNhdXNlZCBzdWNoIGNoYW5nZSBpbiBwYXR0ZXJuIEFpclJFR0kncyBudW1iZXIgb2YgdmlzaXRvcnMgcmF0aGVyIHRoYW4gYW4gZXhvZ2Vub3VzIC8gZXh0ZXJuYWwgZmFjdG9yLiAKCmBgYHtyIGVjaG89RkFMU0UsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD0zLjUsIG91dC53aWR0aD0iMTAwJSJ9CiMgSFBHIFJlc2VydmVkIFZpc2l0b3JzCmFpcjEgPC0gYWlyX3Jlc2VydmUgJT4lCiAgZ3JvdXBfYnkodmlzaXRfZGF0ZSkgJT4lIHN1bW1hcmlzZShyZXNlcnZlX3Zpc2l0X3RvbCA9IHN1bShyZXNlcnZlX3Zpc2l0b3JzKSkKYWlyMiA8LSBhaXJfdmlzaXRfZGF0YSAlPiUgZ3JvdXBfYnkodmlzaXRfZGF0ZSkgJT4lIHN1bW1hcmlzZShhY3RfdmlzaXRfdG9sID0gc3VtKHZpc2l0b3JzKSkKCmhwZzEgPC0gaHBnX3Jlc2VydmUgJT4lCiAgZ3JvdXBfYnkodmlzaXRfZGF0ZSkgJT4lIHN1bW1hcmlzZSh2aXNpdG9yX3RvbCA9IHN1bShyZXNlcnZlX3Zpc2l0b3JzKSkKaHBnMiA8LSBocGdfcmVzZXJ2ZSAlPiUgZ3JvdXBfYnkodmlzaXRfZGF0ZSkgJT4lIGNvdW50KCkKCnJlY3QgPC0gZGF0YS5mcmFtZSh4bWluPWFzLkRhdGUoIjIwMTYtMTItMDEiLCAiJVktJW0tJWQiKSwKICAgICAgICAgICAgICAgICAgIHhtYXg9YXMuRGF0ZSgiMjAxNy0wMS0wMSIsICIlWS0lbS0lZCIpLCB5bWluPS1JbmYsIHltYXg9SW5mKQpyZWN0JHhtZWFuIDwtIGFzLkRhdGUoIjIwMTYtMTItMzEiLCAiJVktJW0tJWQiKQogIApwIDwtIGhwZ19yZXNlcnZlICU+JQogIGdyb3VwX2J5KHZpc2l0X2RhdGUpICU+JSBzdW1tYXJpc2UocmVzZXJ2ZV92aXNpdF90b2wgPSBzdW0ocmVzZXJ2ZV92aXNpdG9ycykpICU+JSAKICBnZ3Bsb3QoKSArCiAgZ2VvbV9saW5lKGFlcyh4PXZpc2l0X2RhdGUsIHk9cmVzZXJ2ZV92aXNpdF90b2wpLCBzaXplPTAuMywgY29sPSdncmV5MjUnKSArCiAgZ2VvbV9iYXIoZGF0YT1yZWN0LCBhZXMoeD14bWVhbiwgeT1JbmYpLCBzdGF0PSdpZGVudGl0eScsCiAgICAgICAgICAgICAgY29sb3VyPSJyZWQiLCBmaWxsPSJyZWQiLCAKICAgICAgICAgICAgICBhbHBoYT0wLjAxLCBzaXplPTEsIGxpbmV0eXBlPTIsIGluaGVyaXQuYWVzID0gRkFMU0UpICsKICBsYWJzKHRpdGxlPSJSZXNlcnZlZCBWaXN0aW9ycyAtIEhvdCBQZXBwZXIgR291cm1ldCIpICsgCiAgc2NhbGVfeF9kYXRlKGRhdGVfYnJlYWtzID0gIjEgbW9udGgiKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTkwLCBoanVzdD0xKSkKZ2dwbG90bHkocCkKYGBgCgojIyBBaXIgVmlzaXRvcnMgVGltZSBTZXJpZXMgRGVjb21wb3NpdGlvbgoKQW55IHRpbWUgc2VyaWVzIGRhdGEgY291bGQgYmUgZGVjb21wb3NlZCBpbnRvIGF0IGxlYXN0IG9uZSBvZiB0aGUgZm9sbG93aW5nIGNvbXBvbmVudHM6IAoKMS4gVHJlbmQgY29tcG9uZW50OiBUaGUgb3ZlcmFsbCB0cmVuZC9wYXR0ZXJuIG9mIHRoZSBzZXJpZXMKMi4gU2Vhc29uYWwgY29tcG9uZW50OiBUaGUgZmx1Y3R1YXRpb25zIGluIHRoZSBkYXRhIHJlbGF0ZWQgdG8gY2FsZW5kYXIgY3ljbGVzCjMuIFJhbmRvbSBjb21wb25lbnQ6IFRoZSByZW1haW5kZXIgdGhhdCBjYW5ub3QgYmUgYXR0cmlidXRlZCB0byBzZWFzb25hbCBvciB0cmVuZCBjb21wb25lbnRzCgpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTN9CmRhaWx5X3Zpc2l0IDwtIGFpcjEgJT4lIGxlZnRfam9pbihhaXIyLCBjKCJ2aXNpdF9kYXRlIikpICU+JSAKICBzZWxlY3QocmVzZXJ2ZV92aXNpdF90b2wpICU+JSAKICB0cyhzdGFydD1jKDIwMTYsMSwxKSwgZnJlcXVlbmN5PTcpCgojIERhaWx5IFZpc2l0IFJhdGVzIERlY29tcG9zaXRpb24KcDIgPC0gZGVjb21wb3NlKGRhaWx5X3Zpc2l0KSAlPiUgLiR0cmVuZCAlPiUKICBhdXRvcGxvdChtYWluPSJBaXIgVmlzaXRvciBUcmVuZCBDb21wb25lbnQiLCB4bGFiPSJEYXkiKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTgpLCBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTgpKQpwMyA8LSBkZWNvbXBvc2UoZGFpbHlfdmlzaXQpICU+JSAuJHNlYXNvbmFsICU+JQogIGF1dG9wbG90KG1haW49IkFpciBWaXNpdG9yIFNlYXNvbmFsIENvbXBvbmVudCIsIHhsYWI9IkRheSIpKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT04KSwgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT04KSkKcDQgPC0gZGVjb21wb3NlKGRhaWx5X3Zpc2l0KSAlPiUgLiRyYW5kb20gJT4lCiAgYXV0b3Bsb3QobWFpbj0iQWlyIFZpc2l0b3IgUmFuZG9tIENvbXBvbmVudCIsIHhsYWI9IkRheSIpKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT04KSwgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT04KSkKCmdyaWQuYXJyYW5nZShwMixwMyxwNCxuY29sPTMpCmBgYAoKVGhlIGtleSBpZGVhIG9mIGEgdGltZSBzZXJpZXMgZm9yZWNhc3QgdGVuZCB0byBmb2N1cyB0aGUgY29uY2VwdCBvZiBhICJzdGF0aW9uYXJ5IiB0aW1lIHNlcmllcy4gQSBzdGF0aW9uYXJ5IHRpbWUgc2VyaWVzIGlzIHdoZW4gdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIG9ic2VydmF0aW9ucyBhdCBkaWZmZXJlbnQgdGltZSBwb2ludHMgYXJlIG5vdCBkZXBlbmRlbnQgb24gYSBzcGVjaWZpYyB0aW1lLCBhbmQgdGhleSB3aWxsIGRpc3RyaWJ1dGUgZXZlbmx5IGFyb3VuZCBhIGNvbnN0YW50IG1lYW4uIE1hbnkgdGltZSBzZXJpZXMgbWF5IGluaXRpYWxseSBzZWVtIHRvIGJlIG5vbi1zdGF0aW9uYXJ5LCBidXQgY2FuIGJlIG1hdGhlbWF0aWNhbCB0cmFuc2Zvcm1lZCBpbnRvIGEgc3RhdGlvbmFyeSB2ZXJzaW9uLiAKCiMjIyBJcyB0aGVyZSBzZWFzb25hbCBwYXR0ZXJuIGluIHRoZSBudW1iZXIgb2YgcmVzZXJ2ZWQgdmlzaXRvcnM/CgojIyMjIEJ5IGhvdXIgb3IgYnkgZGF5IG9mIHdlZWs/CgpPbiBhIG5vbi1ob2xpZGF5LCB0aGVyZSBhcmUgc2lnbmlmaWNhbnRseSBtb3JlIGN1c3RvbWVycyBtYWtpbmcgcmVzZXJ2YXRpb25zIHRvIGVhdCBvdXQgYXQgQWlyUkVHSSBvbiBGcmlkYXkgYW5kIFNhdHVyZGF5LiBIb3dldmVyLCB0aGUgdHJlbmQgZG9lcyBub3QgZm9sbG93IHdoZW4gaXQncyBhIGhvbGlkYXksIHdoZW4gd2Ugc2VlIGEgc2lnbmlmaWNhbnQgZHJvcCBpbiB0aGUgdG90YWwgbnVtYmVyIG9mIHZpc2l0b3JzLiBUaGlzIGlzIGhpZ2hseSBsaWtlbHkgYmVjYXVzZSB0aGVyZSBhcmUgbGVzcyBob2xpZGF5cyB0aGF0IGZhbGwgb24gYSBTYXR1cmRheS4gQXNzdW1pbmcgcGVvcGxlIHdpbGwgYmUgb24gdGltZSBmb3IgdGhlaXIgcmVzZXJ2YXRpb25zLCB3ZSBjb3VsZCBhbHNvIHNlZSB0aGF0IG1vc3Qgb2YgdGhlIHJlc2VydmF0aW9ucyBhcmUgcHJldHR5IGVhcmx5IGZvciBkaW5uZXIgdGltZSwgYXJvdW5kIDU6MDBQTS02OjAwUE0sIGFuZCBub3QgYXJvdW5kIDgtOTozMFBNIChIb3BlZnVsbHkgZXZlcnlvbmUgY2FuIHRyYW5zbGF0ZSB0aGUgbWlsaXRhcnkgdGltZSBpbiB0aGUgY2hhcnQgYmVsb3cgOykgKSAKCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9NSwgb3V0LndpZHRoPSIxMDAlIn0KcDEgPC0gYWlyX3Zpc2l0X2RhdGEgJT4lCiAgZ3JvdXBfYnkodmlzaXRfZG93LCBob2xpZGF5X2ZsZykgJT4lCiAgc3VtbWFyaXNlKHZpc2l0b3JfdG9sID0gc3VtKHZpc2l0b3JzKSkgJT4lCiAgZ2dwbG90KGFlcyhjb2xvcj1ob2xpZGF5X2ZsZykpICsKICBmYWNldF9ncmlkKGhvbGlkYXlfZmxnfi4sIHNjYWxlcz0iZnJlZSIpICsKICBnZW9tX2xpbmUoYWVzKHg9dmlzaXRfZG93LCB5PXZpc2l0b3JfdG9sKSwgZ3JvdXA9MSwgc2l6ZT0wLjMpICsKICBnZW9tX3BvaW50KGFlcyh4PXZpc2l0X2RvdywgeT12aXNpdG9yX3RvbCksIHNpemU9MSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygiIzAwN0QwMCIsICIjQzAwMDAwIikpICsKICBsYWJzKHRpdGxlPSJWaXNpdG9ycyBieSBEYXkgb2YgV2VlayIpKwogIGd1aWRlcyhjb2xvcj1GQUxTRSkKCnAyIDwtIGFpcl9yZXNlcnZlICU+JSBncm91cF9ieShyZXNlcnZlX3RpbWUsIHZpc2l0X2RvdykgJT4lCiAgc3VtbWFyaXNlKHZpc2l0b3JfdG9sID0gc3VtKHJlc2VydmVfdmlzaXRvcnMpKSAlPiUgCiAgZ2dwbG90KCkgKyAKICBnZW9tX2NvbChhZXMoeD1yZXNlcnZlX3RpbWUsIHk9dmlzaXRvcl90b2wpLCBmaWxsPSIjMTk0MTdEIikgKwogIGxhYnModGl0bGU9IlZpc2l0b3JzIGJ5IERheSBvZiBXZWVrICYgVGltZSBTbG90IikrCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcz1zZXEoMCwgNjUwMDAsIGJ5PTEwMDAwKSkgKwogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT05MCwgaGp1c3Q9MSwgdmp1c3Q9MC45KSkKCnN1YnBsb3QocDEscDIsIG1hcmdpbiA9IDAuMDUpCmBgYAoKIyMjIyBPciBkb2VzIGl0IGZvbGxvdyBhIG1vbnRobHkgdHJlbmQ/IAoKQXMgd2UgY291bGQgc2VlIGluIHRoZSBwbG90IGJlbG93LCBNYXJjaCBhbmQgRGVjZW1iZXIsIHdoaWNoIGFyZSBncmVhdGx5IGNvbnRyaWJ1dGVkIHRvIHRoZSBudW1iZXIgb2YgaG9saWRheXMgd2l0aGluIHRoZXNlIHR3byBtb250aHMsIGkuZS4gdGhlIGNoZXJyeSBibG9zc29tIGFuZCBDaHJpc3RtYXMtTllFIHNlYXNvbiBpbiBKYXBhbi4gSW4gYWRkaXRpb24sIEF1Z3VzdCBpbiBTZXB0ZW1iZXIgaGFzIGEgc3VkZGVuIGRyb3AgYXMgd2UgaGF2ZSBvYnNlcnZlZCBpbiB0aGUgYWJvdmUgcGxvdC4gR2l2ZW4gdGhlIGRpZmZlcmVudCBsZXZlbCBpbiBudW1iZXIgb2YgcmVzZXJ2YXRpb25zIGJ5IG1vbnRoLCB3ZSBjb3VsZCBzdXNwZWN0IHRoYXQgdGhlIHRpbWUgc2VyaWVzIGlzIGFsc28gZHJpdmVuIGJ5IGEgc2Vhc29uYWwgY29tcG9uZW50LiAKCmBgYHtyLCBlY2hvPUZBTFNFLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9NSwgb3V0LndpZHRoPSIxMDAlIn0KcCA8LSBhaXJfdmlzaXRfZGF0YSAlPiUgCiAgbXV0YXRlKHZpc2l0X21vbnRoPWZvcm1hdCh2aXNpdF9kYXRlLCIlbSIpKSAlPiUgCiAgZ3JvdXBfYnkodmlzaXRfbW9udGgpICU+JQogIHN1bW1hcmlzZSh2aXNpdG9yX3RvbCA9IHN1bSh2aXNpdG9ycyksIG5faG9saWRheXMgPSBzdW0oYXMubnVtZXJpYyhob2xpZGF5X2ZsZykpKSAlPiUgCiAgZ2dwbG90KCkgKyAKICBnZW9tX2NvbChhZXMoeD12aXNpdF9tb250aCwgeT12aXNpdG9yX3RvbCksIGZpbGw9IiM4Q0I0RTEiKSArCiAgI2dlb21fbGluZShhZXMoeD12aXNpdF9tb250aCwgeT1uX2hvbGlkYXlzKSwgZ3JvdXA9MSwgc2l6ZT0wLjUsIGNvbG9yPSIjMTk0MTdEIikgKwogICNnZW9tX3BvaW50KGFlcyh4PXZpc2l0X21vbnRoLCB5PW5faG9saWRheXMpLCBzaXplPTIsIGNvbG9yPSIjMTk0MTdEIikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWUgPSAiTjEiLCB2YWx1ZXMgPSBjKCJOdW1iZXIgb2YgUmVzZXJ2ZWQgVmlzaXRvcnMiID0gIiM4Q0I0RTEiKSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbChuYW1lID0gIk4yIiwgdmFsdWVzID0gYygiTnVtYmVyIG9mIEhvbGlkYXlzIiA9ICIjMTk0MTdEIikpICsKICBsYWJzKHRpdGxlPSJSZXNlcnZlZCBWaXNpdG9ycyBieSBNb250aCIpCgpnZ3Bsb3RseShwKQojZ3JpZC5hcnJhbmdlKHAxLCBwMiwgbmNvbD0yKQpgYGAKCkluIG9yZGVyIHRvIHVuZGVyc3RhbmQgaG93IHZhcmlhYmxlcyBhdCBkaWZmZXJlbnQgdGltZSBzdGVwcyBhcmUgcmVsYXRlZCwgd2Ugb2Z0ZW4gdXNlIHRoZSBBQ0YgKCoqQXV0b2NvcnJlbGF0aW9uIEZ1bmN0aW9uKiopIGFuZCBQQUNGICgqKlBhcnRpYWwgQXV0b2NvcnJlbGF0aW9uIEZ1bmN0aW9uKiopIHBsb3RzLiBDb250cmFkaWN0b3J5IHRvIHdoYXQgd2Ugc3VzcGVjdGVkIGFib3ZlLCB0aGUgdGltZSBzZXJpZXMgaGF2ZSBpbmdzaWduaWZpY2FudCwgaWYgbm90LCBub24tZXhpc3RlbnQgc2Vhc29uYWwgY29tcG9uZW50IGJhc2VkIG9uIHRoZSBBQ0YgcGxvdCBvZiBhIGRlLXRyZW5kZWQgc2VyaWVzIGJlbG93LiBUaGF0IGJlaW5nIHNhaWQsIHRoZSBzZXJpZXMgYWZ0ZXIgdGhlIHRyYW5zZm9ybWF0aW9uIGNhbiBiZSBkZWZpbmVkIGFzICJzdGF0aW9uYXJ5IiAtIGkuZS4gaXRzIG1lYW4gYW5kIHZhcmlhbmNlIGRvZXMgbm90IHZhcnkgYWNyb3NzIHRpbWUuIEluIHRoZSBzZWNvbmQgQUNGIHBsb3QsIHRoZSBvbmx5IHNpZ25pZmljYW50IHNwaWtlIGlzIGF0IGxhZyAxLCBtZWFuaW5nIHRoYXQgb25seSB0aGUgcGFpciBvZiB0aGUgZXJyb3IgdGVybXMgdGhhdCBhcmUgb25lIGxhZyBkaWZmZXJlbnQgaGFzIHN0cm9uZyBhdXRvY29ycmVsYXRpb24uCiAKCmBgYHtyfQojIGNvbnZlcnQgYWlyX3Jlc2VydmVfdmlzaXQgdG8gdHMKYWlyX3Zpc2l0X3RzIDwtIHRzKGFpcl92aXNpdF9kYXRhWywidmlzaXRvcnMiXSwgc3RhcnQ9YygyMDE2LTAxLTAxKSwgZnJlcXVlbmN5PTcpIAoKIyB0YWtpbmcgdGhlIGZpcnN0IGRpZmZlcmVuY2UKZC5haXJfdmlzaXRfdHMgPC0gZGlmZihhaXJfdmlzaXRfdHMpCmBgYAoKYGBge3IgZWNobz1GQUxTRSwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTZ9CnAxIDwtIGFpcl92aXNpdF90cyAlPiUgCiAgYXV0b3Bsb3QobWFpbj0iTnVtYmVyIG9mIFZpc2l0b3JzIiwgeWxhYj0iIyBvZiBSZXMuVmlzaXRvcnMiLCBsd2Q9MC4yKSsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9OCksIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9OCkpCnAyIDwtIGFjZihhaXJfdmlzaXRfdHMsIHBsb3Q9RkFMU0UsIGxhZy5tYXg9NzIpICU+JSAKICBhdXRvcGxvdChtYWluPSJOdW1iZXIgb2YgVmlzaXRvcnMgLSBBQ0YiLCB5bGFiPSIjIG9mIFJlcy5WaXNpdG9ycyIsIGx3ZD0wLjIpKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT04KSwgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT04KSkKcDMgPC0gZC5haXJfdmlzaXRfdHMgJT4lCiAgYXV0b3Bsb3QobWFpbj0iRGUtdHJlbmRlZCBOdW1iZXIgb2YgVmlzaXRvcnMiLCB5bGFiPSIjIG9mIFJlcy5WaXNpdG9ycyIsIGx3ZD0wLjIpKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT04KSwgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT04KSkKcDQgPC0gYWNmKGQuYWlyX3Zpc2l0X3RzLCBwbG90PUZBTFNFLCBsYWcubWF4PTcyKSAlPiUgCiAgYXV0b3Bsb3QobWFpbj0iRGUtdHJlbmRlZCBOdW1iZXIgb2YgVmlzaXRvcnMgLSBBQ0YiLAogICAgICAgICAgIHlsYWI9IiMgb2YgUmVzLlZpc2l0b3JzIiwgbHdkPTAuMikgKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHM9YygtMC41LDEpKSsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9OCksIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9OCkpCgpncmlkLmFycmFuZ2UocDEsIHAyLCBwMywgcDQsIG5jb2w9MikKYGBgCg==